from datetime import datetime

from flask import request

from flask_jwt_extended import jwt_required
from flask_restx import Namespace, Resource, fields

# 为通用工具函数引入
import os
import re
import stat
from datetime import datetime
from flask import jsonify, request, abort, Response
from sqlalchemy import or_

from app1 import db

from app1.models import PatrolAppConfig

# 创建 Flask-RESTx 命名空间
ns_patrol = Namespace('ns_patrol', description='patrol')


# ========================== 工具：扫描目录 ==========================
def scan_files(dir_path, pattern):
    """
    返回目录下【文件名含 pattern】的文件绝对路径列表（正则）
    """
    if not os.path.isdir(dir_path):
        return []
    # 预编译正则，忽略大小写
    pat = re.compile(pattern, re.I)
    matched = []
    for name in os.listdir(dir_path):
        if pat.search(name):
            matched.append(os.path.join(dir_path, name))
    return matched


# 工具：取文件详细信息
def file_info(fpath):
    st = os.stat(fpath)
    return {
        'abs_path': fpath,
        'name': os.path.basename(fpath),
        'size': st.st_size,
        'mtime': datetime.fromtimestamp(st.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
        'mode': oct(stat.S_IMODE(st.st_mode)),
        'is_readable': os.access(fpath, os.R_OK)
    }


# 工具：安全读取内容（可限制大小）
def read_file_content(fpath, max_kb=500):
    if not os.path.isfile(fpath) or not os.access(fpath, os.R_OK):
        return None
    max_bytes = max_kb * 1024
    try:
        with open(fpath, 'r', encoding='utf-8') as f:
            content = f.read(max_bytes)
            # 判断是否还有剩余内容
            if f.read(1):
                content += '\n...（文件过大，已截断）'
        return content
    except UnicodeDecodeError:
        return '【非文本文件或编码错误，仅支持文本查看】'


# ========================== 输入/输出模型 ==========================
# 单条实体
# patrol_model = ns_patrol.model('PatrolAppConfig', {
#     'id': fields.Integer(required=True, description='主键序号'),
#     'patrol_name': fields.String(required=True, description='巡检名'),
#     'app_name': fields.String(required=True, description='应用名'),
#     'match_alias': fields.String(required=True, description='匹配别名字符'),
#     'remark': fields.String(description='备注'),
#     'create_time': fields.DateTime(description='创建时间'),
#     'update_time': fields.DateTime(description='更新时间')
# })

# 输出模型：DateTime -> String，避免 ISO 格式校验
patrol_model = ns_patrol.model('PatrolAppConfig', {
    'id': fields.Integer(required=True, description='主键序号'),
    'patrol_name': fields.String(required=True, description='巡检名'),
    'app_name': fields.String(required=True, description='应用名'),
    'match_alias': fields.String(required=True, description='匹配别名字符'),
    'remark': fields.String(description='备注'),
    'create_time': fields.String(description='创建时间'),  # <-- 改成 String
    'update_time': fields.String(description='更新时间')  # <-- 改成 String
})

# 统一响应壳
response_patrol_model = ns_patrol.model('Response_Patrol', {
    'message': fields.Nested(ns_patrol.model('Message_Patrol', {
        'result': fields.Integer(description='0=成功 1=失败'),
        'result_text': fields.String(description='结果描述'),
        'count': fields.Integer(description='总记录数'),
        'data': fields.List(fields.Nested(patrol_model))
    }))
})

# 条件 + 分页查询
patrol_query_model = ns_patrol.model('PatrolQuery', {
    'id': fields.Integer(default=0, description='主键序号(0忽略)'),
    'patrol_name': fields.String(default='', description='巡检名(空忽略)'),
    'app_name': fields.String(default='', description='应用名(空忽略)'),
    'match_alias': fields.String(default='', description='匹配别名(空忽略)'),
    'page': fields.Integer(default=1, description='页码'),
    'size': fields.Integer(default=20, description='每页条数')
})

# 新增
patrol_add_model = ns_patrol.model('PatrolAdd', {
    'patrol_name': fields.String(required=True, description='巡检名'),
    'app_name': fields.String(required=True, description='应用名'),
    'match_alias': fields.String(required=True, description='匹配别名字符'),
    'remark': fields.String(description='备注', default='')
})

# ========================== 0. 修改入参模型 ==========================
# 删除
patrol_del_model = ns_patrol.model('PatrolDelete', {
    'patrol_name': fields.String(required=True, description='巡检名')
})

# 更新
patrol_upd_model = ns_patrol.model('PatrolUpdate', {
    'patrol_name': fields.String(required=True, description='巡检名（作为定位键）'),
    'app_name': fields.String(required=True, description='应用名'),
    'match_alias': fields.String(required=True, description='匹配别名字符'),
    'remark': fields.String(description='备注', default='')
})

# 单条查询（by patrol_name）
patrol_get_model = ns_patrol.model('PatrolGet', {
    'patrol_name': fields.String(required=True, description='巡检名')
})

# ========== 新接口模型 ==========
file_item_model = ns_patrol.model('FileItem', {
    'abs_path': fields.String(description='绝对路径'),
    'name': fields.String(description='文件名'),
    'size': fields.Integer(description='字节大小'),
    'mtime': fields.String(description='修改时间'),
    'mode': fields.String(description='权限位'),
    'is_readable': fields.Boolean(description='是否可读')
})

file_list_model = ns_patrol.model('FileListResp', {
    'message': fields.Nested(ns_patrol.model('MsgFileList', {
        'result': fields.Integer,
        'result_text': fields.String,
        'count': fields.Integer,
        'data': fields.List(fields.Nested(file_item_model))
    }))
})

file_content_model = ns_patrol.model('FileContentResp', {
    'message': fields.Nested(ns_patrol.model('MsgFileContent', {
        'result': fields.Integer,
        'result_text': fields.String,
        'count': fields.Integer,
        'data': fields.List(fields.String(description='文件内容行'))
    }))
})

# 入参：统一用 patrol_name 定位
file_base_model = ns_patrol.model('FileBase', {
    'patrol_name': fields.String(required=True, description='巡检名'),
    'max_kb': fields.Integer(default=500, description='最大读取 KB（仅内容接口）')
})

# ========================= 汇总查询 =====================================
# 汇总文件项（带所属巡检）
agg_file_model = ns_patrol.model('AggFileItem', {
    'patrol_name': fields.String(description='所属巡检名'),
    'match_alias': fields.String(description='匹配别名'),
    'file_list': fields.List(fields.Nested(file_item_model))  # 复用前面 file_info 结构
})

agg_resp_model = ns_patrol.model('AggFileResp', {
    'message': fields.Nested(ns_patrol.model('MsgAgg', {
        'result': fields.Integer,
        'result_text': fields.String,
        'count': fields.Integer(description='总文件数'),
        'data': fields.List(fields.Nested(agg_file_model))
    }))
})


# ========================== 多值查询模型 ==========================
# 多值删除模型
patrol_mult_del_model = ns_patrol.model('PatrolMultDelete', {
    'patrol_names': fields.List(fields.String, required=True, description='巡检名列表', default=[])
})

# 多值查询模型
patrol_mult_get_model = ns_patrol.model('PatrolMultGet', {
    'patrol_names': fields.List(fields.String, required=True, description='巡检名列表', default=[])
})

# 多值文件查询模型
file_mult_base_model = ns_patrol.model('FileMultBase', {
    'patrol_names': fields.List(fields.String, required=True, description='巡检名列表', default=[]),
    'max_kb': fields.Integer(default=500, description='最大读取 KB（仅内容接口）')
})

# 多值分页查询模型
patrol_mult_page_model = ns_patrol.model('PatrolMultPage', {
    'page': fields.Integer(default=1, description='页码'),
    'size': fields.Integer(default=20, description='每页条数'),
    'patrol_names': fields.List(fields.String, description='巡检名列表（多值）', default=[]),
    'app_names': fields.List(fields.String, description='应用名列表（多值）', default=[]),
    'match_aliases': fields.List(fields.String, description='匹配别名列表（多值）', default=[])
})

# ========================== 1. 分页+条件查询 ==========================
@ns_patrol.route('/patrol_list')
class PatrolListResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_query_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """条件+分页查询（字段空/0 表示忽略）"""
        data = request.get_json()

        query = PatrolAppConfig.query
        if data.get('id', 0) != 0:
            query = query.filter_by(id=data['id'])
        if data.get('patrol_name'):
            query = query.filter(PatrolAppConfig.patrol_name.like(f"%{data['patrol_name']}%"))
        if data.get('app_name'):
            query = query.filter(PatrolAppConfig.app_name.like(f"%{data['app_name']}%"))
        if data.get('match_alias'):
            query = query.filter(PatrolAppConfig.match_alias.like(f"%{data['match_alias']}%"))

        page = max(1, data.get('page', 1))
        size = max(1, data.get('size', 20))
        total = query.count()
        rows = query.limit(size).offset((page - 1) * size).all()

        return {
            'message': {
                'result': 0,
                'result_text': '查询成功',
                'count': total,
                'data': [r.to_dict() for r in rows]
            }
        }


# ========================== 2. 新增 ==========================
@ns_patrol.route('/patrol_create')
class PatrolCreateResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_add_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """新增映射（patrol_name 全局唯一）"""
        data = request.get_json()

        required = ['patrol_name', 'app_name', 'match_alias']
        missing = [f for f in required if not data.get(f)]
        if missing:
            return {
                'message': {
                    'result': 1,
                    'result_text': f'以下字段不能为空: {", ".join(missing)}',
                    'count': 0, 'data': []
                }
            }, 400

        # 1. 先校验 patrol_name 全局唯一
        if PatrolAppConfig.query.filter_by(patrol_name=data['patrol_name']).first():
            return {
                'message': {
                    'result': 1,
                    'result_text': f'巡检名 "{data["patrol_name"]}" 已存在',
                    'count': 0, 'data': []
                }
            }, 400

        # 2. 再校验 巡检+应用 组合唯一（可选，保留防双保险）
        exist = PatrolAppConfig.query.filter_by(patrol_name=data['patrol_name'],
                                                app_name=data['app_name']).first()
        if exist:  # 理论上第1步已拦住，仍保留
            return {
                'message': {
                    'result': 1,
                    'result_text': '该巡检+应用映射已存在',
                    'count': 0, 'data': []
                }
            }, 400

        new_row = PatrolAppConfig(
            patrol_name=data['patrol_name'],
            app_name=data['app_name'],
            match_alias=data['match_alias'],
            remark=data.get('remark', '')
        )
        try:
            db.session.add(new_row)
            db.session.commit()
            return {
                'message': {
                    'result': 0,
                    'result_text': '创建成功',
                    'count': 1,
                    'data': [new_row.to_dict()]
                }
            }, 201
        except Exception as e:
            db.session.rollback()
            return {
                'message': {
                    'result': 1,
                    'result_text': f'创建失败: {str(e)}',
                    'count': 0, 'data': []
                }
            }, 500


# ========================== 3. 删除（by patrol_name） ==========================
@ns_patrol.route('/patrol_delete')
class PatrolDeleteResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_del_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """根据 patrol_name 删除整条映射"""
        data = request.get_json()
        row = PatrolAppConfig.query.filter_by(patrol_name=data['patrol_name']).first()
        if not row:
            return {
                'message': {
                    'result': 1,
                    'result_text': '巡检名不存在',
                    'count': 0, 'data': []
                }
            }, 404

        try:
            db.session.delete(row)
            db.session.commit()
            return {
                'message': {
                    'result': 0,
                    'result_text': '删除成功',
                    'count': 1,
                    'data': [row.to_dict()]
                }
            }
        except Exception as e:
            db.session.rollback()
            return {
                'message': {
                    'result': 1,
                    'result_text': f'删除失败: {str(e)}',
                    'count': 0, 'data': []
                }
            }, 500


# ========================== 更新（仅 patrol_name 唯一，app_name 可重复） ==========
@ns_patrol.route('/patrol_update')
class PatrolUpdateResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_upd_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """根据 patrol_name 更新其余字段（patrol_name 只定位，不改；app_name 可重复）"""
        data = request.get_json()

        required = ['app_name', 'match_alias']
        missing = [f for f in required if not data.get(f)]
        if missing:
            return {
                'message': {
                    'result': 1,
                    'result_text': f'以下字段不能为空: {", ".join(missing)}',
                    'count': 0, 'data': []
                }
            }, 400

        row = PatrolAppConfig.query.filter_by(patrol_name=data['patrol_name']).first()
        if not row:
            return {
                'message': {
                    'result': 1,
                    'result_text': '巡检名不存在',
                    'count': 0, 'data': []
                }
            }, 404

        # 直接更新，不再校验 app_name 唯一
        try:
            row.app_name = data['app_name']
            row.match_alias = data['match_alias']
            row.remark = data.get('remark', '')
            row.update_time = datetime.utcnow()
            db.session.commit()
            return {
                'message': {
                    'result': 0,
                    'result_text': '更新成功',
                    'count': 1,
                    'data': [row.to_dict()]
                }
            }
        except Exception as e:
            db.session.rollback()
            return {
                'message': {
                    'result': 1,
                    'result_text': f'更新失败: {str(e)}',
                    'count': 0, 'data': []
                }
            }, 500


# ========================== 5. 单条查询（by patrol_name） ==========================
@ns_patrol.route('/patrol_get')
class PatrolGetResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_get_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """根据 patrol_name 返回单条详细信息"""
        data = request.get_json()
        patrol_name = data.get('patrol_name', '').strip()
        if not patrol_name:
            return {
                'message': {
                    'result': 1,
                    'result_text': 'patrol_name 不能为空',
                    'count': 0,
                    'data': []
                }
            }, 400

        row = PatrolAppConfig.query.filter_by(patrol_name=patrol_name).first()
        if not row:
            return {
                'message': {
                    'result': 1,
                    'result_text': '巡检名不存在',
                    'count': 0,
                    'data': []
                }
            }, 404

        return {
            'message': {
                'result': 0,
                'result_text': '查询成功',
                'count': 1,
                'data': [row.to_dict()]  # 统一放在 list 里
            }
        }


# ========================== 1. 文件列表（正则含 match_alias） ==========================
@ns_patrol.route('/file_list')
class FileListResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(file_base_model)
    @ns_patrol.marshal_with(file_list_model)
    def post(self):
        """返回目录下文件名含【match_alias】的文件列表"""
        data = request.get_json()
        patrol_name = data.get('patrol_name', '').strip()
        if not patrol_name:
            return {'message': {'result': 1, 'result_text': 'patrol_name 不能为空', 'count': 0, 'data': []}}, 400

        cfg = PatrolAppConfig.query.filter_by(patrol_name=patrol_name).first()
        if not cfg:
            return {'message': {'result': 1, 'result_text': '巡检名不存在', 'count': 0, 'data': []}}, 404

        # 从 .env 读取根目录；也可写死
        root_dir = os.getenv('PATROL_PATH', r'/HX/HX')
        print(root_dir)
        if not os.path.isdir(root_dir):
            return {'message': {'result': 1, 'result_text': '目录不存在', 'count': 0, 'data': []}}, 400

        files = scan_files(root_dir, cfg.match_alias)
        infos = [file_info(f) for f in files]

        return {
            'message': {
                'result': 0,
                'result_text': '查询成功',
                'count': len(infos),
                'data': infos
            }
        }


# ========================== 2. 文件详细信息 ==========================
@ns_patrol.route('/file_info')
class FileInfoResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(file_base_model)
    @ns_patrol.marshal_with(file_list_model)  # 复用同一模型
    def post(self):
        """返回含 match_alias 的所有文件详细信息"""
        data = request.get_json()
        patrol_name = data.get('patrol_name', '').strip()
        if not patrol_name:
            return {'message': {'result': 1, 'result_text': 'patrol_name 不能为空', 'count': 0, 'data': []}}, 400

        cfg = PatrolAppConfig.query.filter_by(patrol_name=patrol_name).first()
        if not cfg:
            return {'message': {'result': 1, 'result_text': '巡检名不存在', 'count': 0, 'data': []}}, 404

        root_dir = os.getenv('PATROL_PATH', r'/HX/HX')
        if not os.path.isdir(root_dir):
            return {'message': {'result': 1, 'result_text': '目录不存在', 'count': 0, 'data': []}}, 400

        files = scan_files(root_dir, cfg.match_alias)
        infos = [file_info(f) for f in files]

        return {
            'message': {
                'result': 0,
                'result_text': '查询成功',
                'count': len(infos),
                'data': infos
            }
        }


# ========================== 3. 读取文件内容（最晚文件 + 文件名拼 text） ==========
@ns_patrol.route('/file_content')
class FileContentResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(file_base_model)
    def post(self):
        """读取【最晚生成匹配文件】的前 N KB 内容（文本）"""
        data = request.get_json()
        patrol_name = data.get('patrol_name', '').strip()
        max_kb = max(1, data.get('max_kb', 500))

        if not patrol_name:
            return {'message': {'result': 1, 'result_text': f'patrol_name 不能为空', 'count': 0, 'data': []}}, 400

        cfg = PatrolAppConfig.query.filter_by(patrol_name=patrol_name).first()
        if not cfg:
            return {'message': {'result': 1, 'result_text': f'巡检名不存在', 'count': 0, 'data': []}}, 404

        root_dir = os.getenv('PATROL_PATH', r'/HX/HX')
        if not os.path.isdir(root_dir):
            return {'message': {'result': 1, 'result_text': f'目录不存在', 'count': 0, 'data': []}}, 400

        files = scan_files(root_dir, cfg.match_alias)
        if not files:
            return {'message': {'result': 1, 'result_text': f'无匹配文件', 'count': 0, 'data': []}}, 404

        # 按修改时间降序，取最晚（最新）文件
        latest_file = max(files, key=os.path.getmtime)
        fpath = latest_file
        content = read_file_content(fpath, max_kb)
        file_name = os.path.basename(fpath)   # 统一文件名

        if content is None:
            return {'message': {'result': 1, 'result_text': f'文件不可读或非文本：{file_name}', 'count': 0, 'data': []}}, 400

        lines = content.splitlines()
        return {
            'message': {
                'result': 0,
                'result_text': f'读取成功：{file_name}',   # ← 拼接文件名
                'count': len(lines),
                'data': lines
            }
        }


# ========================== 4. 汇总所有配置的文件列表 ==========================
@ns_patrol.route('/file_all')
class FileAllResource(Resource):
    # @jwt_required()
    @ns_patrol.marshal_with(agg_resp_model)
    def get(self):
        """返回 **所有 PatrolAppConfig 记录** 匹配到的文件汇总列表"""
        root_dir = os.getenv('PATROL_PATH', r'/HX/HX')
        if not os.path.isdir(root_dir):
            return {'message': {'result': 1, 'result_text': '目录不存在', 'count': 0, 'data': []}}, 400

        configs = PatrolAppConfig.query.all()
        if not configs:
            return {'message': {'result': 1, 'result_text': '无配置记录', 'count': 0, 'data': []}}, 404

        agg = []
        total_files = 0
        for cfg in configs:
            files = scan_files(root_dir, cfg.match_alias)
            infos = [file_info(f) for f in files]
            agg.append({
                'patrol_name': cfg.patrol_name,
                'match_alias': cfg.match_alias,
                'file_list': infos
            })
            total_files += len(infos)

        return {
            'message': {
                'result': 0,
                'result_text': '汇总成功',
                'count': total_files,
                'data': agg
            }
        }


# # ========================== 4. 汇总所有配置的文件列表 ==========================
# @ns_patrol.route('/file_all')
# class FileAllResource(Resource):
#     # @jwt_required()
#     @ns_patrol.marshal_with(agg_resp_model)
#     def get(self):
#         """返回 **所有 PatrolAppConfig 记录** 匹配到的文件汇总列表"""
#         root_dir = os.getenv('PATROL_PATH', r'/HX/HX')
#         if not os.path.isdir(root_dir):
#             return {'message': {'result': 1, 'result_text': '目录不存在', 'count': 0, 'data': []}}, 400
#
#         configs = PatrolAppConfig.query.all()
#         if not configs:
#             return {'message': {'result': 1, 'result_text': '无配置记录', 'count': 0, 'data': []}}, 404
#
#         agg = []
#         total_files = 0
#         for cfg in configs:
#             files = scan_files(root_dir, cfg.match_alias)
#             infos = [file_info(f) for f in files]
#             agg.append({
#                 'patrol_name': cfg.patrol_name,
#                 'match_alias': cfg.match_alias,
#                 'file_list': infos
#             })
#             total_files += len(infos)
#
#         return {
#             'message': {
#                 'result': 0,
#                 'result_text': '汇总成功',
#                 'count': total_files,
#                 'data': agg
#             }
#         }


# ========================== 新增多值查询接口 ==========================

@ns_patrol.route('/patrol_delete_mult')
class PatrolDeleteMultResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_mult_del_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """批量删除 - 根据巡检名列表删除多条映射"""
        data = request.get_json()
        patrol_names = data.get('patrol_names', [])

        if not patrol_names:
            return {
                'message': {
                    'result': 1,
                    'result_text': 'patrol_names 不能为空',
                    'count': 0,
                    'data': []
                }
            }, 400

        # 查询所有匹配的记录
        rows = PatrolAppConfig.query.filter(PatrolAppConfig.patrol_name.in_(patrol_names)).all()

        if not rows:
            return {
                'message': {
                    'result': 1,
                    'result_text': '未找到匹配的记录',
                    'count': 0,
                    'data': []
                }
            }, 404

        deleted_data = []
        try:
            for row in rows:
                deleted_data.append(row.to_dict())
                db.session.delete(row)
            db.session.commit()

            return {
                'message': {
                    'result': 0,
                    'result_text': f'批量删除成功，共删除 {len(rows)} 条记录',
                    'count': len(rows),
                    'data': deleted_data
                }
            }
        except Exception as e:
            db.session.rollback()
            return {
                'message': {
                    'result': 1,
                    'result_text': f'批量删除失败: {e}',
                    'count': 0,
                    'data': []
                }
            }, 500


@ns_patrol.route('/patrol_get_mult')
class PatrolGetMultResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_mult_get_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """批量查询 - 根据巡检名列表查询多条记录"""
        data = request.get_json()
        patrol_names = data.get('patrol_names', [])

        if not patrol_names:
            return {
                'message': {
                    'result': 1,
                    'result_text': 'patrol_names 不能为空',
                    'count': 0,
                    'data': []
                }
            }, 400

        # 查询所有匹配的记录
        rows = PatrolAppConfig.query.filter(PatrolAppConfig.patrol_name.in_(patrol_names)).all()

        return {
            'message': {
                'result': 0,
                'result_text': f'查询成功，共找到 {len(rows)} 条记录',
                'count': len(rows),
                'data': [r.to_dict() for r in rows]
            }
        }


@ns_patrol.route('/file_list_mult')
class FileListMultResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(file_mult_base_model)
    @ns_patrol.marshal_with(file_list_model)
    def post(self):
        """多值文件列表 - 根据多个巡检名返回文件列表"""
        data = request.get_json()
        patrol_names = data.get('patrol_names', [])

        if not patrol_names:
            return {'message': {'result': 1, 'result_text': 'patrol_names 不能为空', 'count': 0, 'data': []}}, 400

        # 查询所有匹配的配置
        configs = PatrolAppConfig.query.filter(PatrolAppConfig.patrol_name.in_(patrol_names)).all()

        if not configs:
            return {'message': {'result': 1, 'result_text': '未找到匹配的巡检配置', 'count': 0, 'data': []}}, 404

        root_dir = os.getenv('PATROL_PATH', r'/HX/HX')
        if not os.path.isdir(root_dir):
            return {'message': {'result': 1, 'result_text': '目录不存在', 'count': 0, 'data': []}}, 400

        all_infos = []
        for cfg in configs:
            files = scan_files(root_dir, cfg.match_alias)
            infos = [file_info(f) for f in files]
            all_infos.extend(infos)

        return {
            'message': {
                'result': 0,
                'result_text': '多值文件列表查询成功',
                'count': len(all_infos),
                'data': all_infos
            }
        }


@ns_patrol.route('/file_info_mult')
class FileInfoMultResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(file_mult_base_model)
    @ns_patrol.marshal_with(file_list_model)
    def post(self):
        """多值文件信息 - 根据多个巡检名返回文件详细信息"""
        data = request.get_json()
        patrol_names = data.get('patrol_names', [])

        if not patrol_names:
            return {'message': {'result': 1, 'result_text': 'patrol_names 不能为空', 'count': 0, 'data': []}}, 400

        # 查询所有匹配的配置
        configs = PatrolAppConfig.query.filter(PatrolAppConfig.patrol_name.in_(patrol_names)).all()

        if not configs:
            return {'message': {'result': 1, 'result_text': '未找到匹配的巡检配置', 'count': 0, 'data': []}}, 404

        root_dir = os.getenv('PATROL_PATH', r'/HX/HX')
        if not os.path.isdir(root_dir):
            return {'message': {'result': 1, 'result_text': '目录不存在', 'count': 0, 'data': []}}, 400

        all_infos = []
        for cfg in configs:
            files = scan_files(root_dir, cfg.match_alias)
            infos = [file_info(f) for f in files]
            all_infos.extend(infos)

        return {
            'message': {
                'result': 0,
                'result_text': '多值文件信息查询成功',
                'count': len(all_infos),
                'data': all_infos
            }
        }


@ns_patrol.route('/patrol_list_mult')
class PatrolListMultResource(Resource):
    # @jwt_required()
    @ns_patrol.expect(patrol_mult_page_model)
    @ns_patrol.marshal_with(response_patrol_model)
    def post(self):
        """多值分页查询 - 支持巡检名、应用名、匹配别名的多值查询"""
        data = request.get_json()
        page = max(1, data.get('page', 1))
        size = max(1, data.get('size', 20))

        query = PatrolAppConfig.query

        # 多值条件过滤
        patrol_names = data.get('patrol_names', [])
        app_names = data.get('app_names', [])
        match_aliases = data.get('match_aliases', [])

        # 构建多值查询条件
        conditions = []

        if patrol_names:
            conditions.append(PatrolAppConfig.patrol_name.in_(patrol_names))

        if app_names:
            conditions.append(PatrolAppConfig.app_name.in_(app_names))

        if match_aliases:
            conditions.append(PatrolAppConfig.match_alias.in_(match_aliases))

        # 如果有多个条件，使用 OR 连接
        if conditions:
            query = query.filter(or_(*conditions))

        total = query.count()
        rows = query.limit(size).offset((page - 1) * size).all()

        return {
            'message': {
                'result': 0,
                'result_text': '多值分页查询成功',
                'count': total,
                'data': [r.to_dict() for r in rows]
            }
        }